﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_COLOR_H
#define _UXS_COLOR_H

#pragma once

#include "TVec.h"
#include "Rand/Random.h"
#include "../Containers/TString.h"

namespace UX_STL
{
namespace UXMath
{


	//
	// Color Functions
	//

	struct ColorUtils
	{
	// types
	private:
		template <typename T>
		struct Limit
		{
			template <bool bFloat>	static T _Max()	{ return bFloat ? T(1) : TypeInfo<T>::Max(); }
									static T Max()	{ return _Max< TypeDescriptor::IsFloat<T>::value >(); }
									static T Min()	{ return T(0); }
		};

		template <typename T>
		struct LuminanceK {
			//static TVec<T,3>	Get()				{ return TVec<T,3>( 0.27,   0.67,   0.06   ); }
			//static TVec<T,3>	Get()				{ return TVec<T,3>( 0.3,    0.59,   0.11   ); }
			static TVec<T,3>	Get()				{ return TVec<T,3>( 0.2126, 0.7152, 0.0722 ); }
		};


	// methods
	public:
		template <typename T, usize I>	static	TVec<T,I>	Saturate (const TVec<T,I> &v);
		template <typename T, usize I>	static	TVec<T,I>	Normalize (const TVec<T,I> &v);

		template <typename T, usize I>	static	TVec<T,I>	Random ();
		template <typename T, usize I>	static	TVec<T,I>	Random (const TVec<T,I> &min, const TVec<T,I> &max);

		template <typename T, usize I>	static	T			Luminance (const TVec<T,I> &v);
		template <typename T, usize I>	static	T			LuminanceNorm (const TVec<T,I> &v);

		template <typename T, usize I>	static	T			Negative (const TVec<T,I> &v);
		template <typename T, usize I>	static	T			NegativeAlpha (const TVec<T,I> &v);
		template <typename T, usize I>	static	T			AdjustContrast (const TVec<T,I> &v, T k);
		template <typename T, usize I>	static	T			AdjustSaturation (const TVec<T,I> &v, T k);
		
		// conversion to non linear color space
		template <typename T, usize I>	static	TVec<T,I>	ToSRGB (const TVec<T,I> &v);
		template <typename T, usize I>	static	TVec<T,I>	FromSRGB (const TVec<T,I> &v);

		template <typename T>			static	TVec<T,4>	FromRGBA8 (uint32 uRGBA);
		template <typename T>			static	uint32		ToRGBA8 (const TVec<T,4> &v);

		template <typename B, typename T, usize I>
										static	TVec<B,I>	Convert (const TVec<T,I> &v);

		template <typename T>		static	TMatrix<T,4,4>	SaturationMat (const T s);
	};


	//
	// Color4
	//

	struct color4u : u8_vec4
	{
	// methods
	public:
		color4u () {}
		color4u (const color4f &c);
		
		explicit
		color4u (uint8 val): u8_vec4( val ) {}

		explicit
		color4u (int val): u8_vec4( uint8(val) ) {}
		
		explicit
		color4u (const uint8 *p): u8_vec4( p ) {}

		color4u (uint8 R, uint8 G, uint8 B, uint8 A = 255): u8_vec4( R, G, B, A) {}

		template <typename T>
		color4u (const TVec<T,4> &v): u8_vec4( ColorUtils::Convert<uint8>( v ) ) {}

		operator u8_vec4 & ()					{ return ReferenceCast< u8_vec4 >( *this ); }
		operator u8_vec4   () const				{ return u8_vec4( x, y, z, w ); }

		uint		ToIntRGBA () const			{ return ReferenceCast<uint>( ABGR() ); }
		uint		ToIntARGB () const			{ return ReferenceCast<uint>( ARGB() ); }
		uint		ToIntABGR () const			{ return ReferenceCast<uint>( *this ); }
		uint		ToIntRGB  () const			{ return ToIntRGBA() >> 8; }

		color4u &	FromIntABGR (uint abgr)		{ *this = ReferenceCast<color4u>( abgr );  return *this; }
		color4u &	FromIntARGB (uint argb)		{ *this = ReferenceCast<color4u>( argb ).BGRA();  return *this; }
		color4u &	FromIntRGBA (uint rgba)		{ *this = ReferenceCast<color4u>( rgba ).ABGR();  return *this; }
		color4u &	FromIntRGB  (uint rgb)		{ return FromIntRGBA( (rgb << 8) | 0xFF ); }

		value_t		R ()	const				{ return (*this)[0]; }
		value_t &	R ()						{ return (*this)[0]; }
		value_t		G ()	const				{ return (*this)[1]; }
		value_t &	G ()						{ return (*this)[1]; }
		value_t		B ()	const				{ return (*this)[2]; }
		value_t &	B ()						{ return (*this)[2]; }
		value_t		A ()	const				{ return (*this)[3]; }
		value_t &	A ()						{ return (*this)[3]; }

		color4u		BGRA ()	const				{ return color4u( z, y, x, w ); }
		color4u		ABGR ()	const				{ return color4u( w, z, y, x ); }
		color4u		ARGB () const				{ return color4u( w, x, y, z ); }

		color4u operator * (float right) const
		{
			return color4u( ColorUtils::Convert<float>( *this ) * right );
		}

		static color4u Interpolate (color4u left, color4u right, float a)
		{
			const fvec4 l = ColorUtils::Convert<float>( left );
			const fvec4 r = ColorUtils::Convert<float>( right );
			return color4u( Mix( l, r, a ) );
		}


	// types
	public:
		struct Default
		{
			static color4u	Black ()	{ return color4u(   0,   0,   0, 255 ); }
			static color4u	White ()	{ return color4u( 255, 255, 255, 255 ); }
			static color4u	Gray ()		{ return color4u( 192, 192, 192, 255 ); }
			static color4u	Red ()		{ return color4u( 255,   0,   0, 255 ); }
			static color4u	Green ()	{ return color4u(   0, 255,   0, 255 ); }
			static color4u	Blue ()		{ return color4u(   0,   0, 255, 255 ); }
			static color4u	Yellow ()	{ return color4u( 255, 255,   0, 255 ); }
		};

		struct Html
		{
#			define __DEF_COLOR( _name, _color )		static color4u	_name () { return color4u().FromIntRGB( _color ); }

			__DEF_COLOR( AliceBlue,				0xF0F8FF )
			__DEF_COLOR( AntiqueWhite,			0xFAEBD7 )
			__DEF_COLOR( Aqua,					0x00FFFF )
			__DEF_COLOR( Aquamarine,			0x7FFFD4 )
			__DEF_COLOR( Azure,					0xF0FFFF )
			__DEF_COLOR( Beige,					0xF5F5DC )
			__DEF_COLOR( Bisque,				0xFFE4C4 )
			__DEF_COLOR( Black,					0x000000 )
			__DEF_COLOR( BlanchedAlmond,		0xFFEBCD )
			__DEF_COLOR( Blue,					0x0000FF )
			__DEF_COLOR( BlueViolet,			0x8A2BE2 )
			__DEF_COLOR( Brown,					0xA52A2A )
			__DEF_COLOR( BurlyWood,				0xDEB887 )
			__DEF_COLOR( CadetBlue,				0x5F9EA0 )
			__DEF_COLOR( Chartreuse,			0x7FFF00 )
			__DEF_COLOR( Chocolate,				0xD2691E )
			__DEF_COLOR( Coral,					0xFF7F50 )
			__DEF_COLOR( CornflowerBlue,		0x6495ED )
			__DEF_COLOR( Cornsilk,				0xFFF8DC )
			__DEF_COLOR( Crimson,				0xDC143C )
			__DEF_COLOR( Cyan,					0x00FFFF )
			__DEF_COLOR( DarkBlue,				0x00008B )
			__DEF_COLOR( DarkCyan,				0x008B8B )
			__DEF_COLOR( DarkGoldenRod,			0xB8860B )
			__DEF_COLOR( DarkGray,				0xA9A9A9 )
			__DEF_COLOR( DarkGreen,				0x006400 )
			__DEF_COLOR( DarkKhaki,				0xBDB76B )
			__DEF_COLOR( DarkMagenta,			0x8B008B )
			__DEF_COLOR( DarkOliveGreen,		0x556B2F )
			__DEF_COLOR( DarkOrange,			0xFF8C00 )
			__DEF_COLOR( DarkOrchid,			0x9932CC )
			__DEF_COLOR( DarkRed,				0x8B0000 )
			__DEF_COLOR( DarkSalmon,			0xE9967A )
			__DEF_COLOR( DarkSeaGreen,			0x8FBC8F )
			__DEF_COLOR( DarkSlateBlue,			0x483D8B )
			__DEF_COLOR( DarkSlateGray,			0x2F4F4F )
			__DEF_COLOR( DarkTurquoise,			0x00CED1 )
			__DEF_COLOR( DarkViolet,			0x9400D3 )
			__DEF_COLOR( DeepPink,				0xFF1493 )
			__DEF_COLOR( DeepSkyBlue,			0x00BFFF )
			__DEF_COLOR( DimGray,				0x696969 )
			__DEF_COLOR( DodgerBlue,			0x1E90FF )
			__DEF_COLOR( FireBrick,				0xB22222 )
			__DEF_COLOR( FloralWhite,			0xFFFAF0 )
			__DEF_COLOR( ForestGreen,			0x228B22 )
			__DEF_COLOR( Fuchsia,				0xFF00FF )
			__DEF_COLOR( Gainsboro,				0xDCDCDC )
			__DEF_COLOR( GhostWhite,			0xF8F8FF )
			__DEF_COLOR( Gold,					0xFFD700 )
			__DEF_COLOR( GoldenRod,				0xDAA520 )
			__DEF_COLOR( Gray,					0x808080 )
			__DEF_COLOR( Green,					0x008000 )
			__DEF_COLOR( GreenYellow,			0xADFF2F )
			__DEF_COLOR( HoneyDew,				0xF0FFF0 )
			__DEF_COLOR( HotPink,				0xFF69B4 )
			__DEF_COLOR( IndianRed,				0xCD5C5C )
			__DEF_COLOR( Indigo,				0x4B0082 )
			__DEF_COLOR( Ivory,					0xFFFFF0 )
			__DEF_COLOR( Khaki,					0xF0E68C )
			__DEF_COLOR( Lavender,				0xE6E6FA )
			__DEF_COLOR( LavenderBlush,			0xFFF0F5 )
			__DEF_COLOR( LawnGreen,				0x7CFC00 )
			__DEF_COLOR( LemonChiffon,			0xFFFACD )
			__DEF_COLOR( LightBlue,				0xADD8E6 )
			__DEF_COLOR( LightCoral,			0xF08080 )
			__DEF_COLOR( LightCyan,				0xE0FFFF )
			__DEF_COLOR( LightGoldenRodYellow,	0xFAFAD2 )
			__DEF_COLOR( LightGray,				0xD3D3D3 )
			__DEF_COLOR( LightGreen,			0x90EE90 )
			__DEF_COLOR( LightPink,				0xFFB6C1 )
			__DEF_COLOR( LightSalmon,			0xFFA07A )
			__DEF_COLOR( LightSeaGreen,			0x20B2AA )
			__DEF_COLOR( LightSkyBlue,			0x87CEFA )
			__DEF_COLOR( LightSlateGray,		0x778899 )
			__DEF_COLOR( LightSteelBlue,		0xB0C4DE )
			__DEF_COLOR( LightYellow,			0xFFFFE0 )
			__DEF_COLOR( Lime,					0x00FF00 )
			__DEF_COLOR( LimeGreen,				0x32CD32 )
			__DEF_COLOR( Linen,					0xFAF0E6 )
			__DEF_COLOR( Magenta,				0xFF00FF )
			__DEF_COLOR( Maroon,				0x800000 )
			__DEF_COLOR( MediumAquaMarine,		0x66CDAA )
			__DEF_COLOR( MediumBlue,			0x0000CD )
			__DEF_COLOR( MediumOrchid,			0xBA55D3 )
			__DEF_COLOR( MediumPurple,			0x9370DB )
			__DEF_COLOR( MediumSeaGreen,		0x3CB371 )
			__DEF_COLOR( MediumSlateBlue,		0x7B68EE )
			__DEF_COLOR( MediumSpringGreen,		0x00FA9A )
			__DEF_COLOR( MediumTurquoise,		0x48D1CC )
			__DEF_COLOR( MediumVioletRed,		0xC71585 )
			__DEF_COLOR( MidnightBlue,			0x191970 )
			__DEF_COLOR( MintCream,				0xF5FFFA )
			__DEF_COLOR( MistyRose,				0xFFE4E1 )
			__DEF_COLOR( Moccasin,				0xFFE4B5 )
			__DEF_COLOR( NavajoWhite,			0xFFDEAD )
			__DEF_COLOR( Navy,					0x000080 )
			__DEF_COLOR( OldLace,				0xFDF5E6 )
			__DEF_COLOR( Olive,					0x808000 )
			__DEF_COLOR( OliveDrab,				0x6B8E23 )
			__DEF_COLOR( Orange,				0xFFA500 )
			__DEF_COLOR( OrangeRed,				0xFF4500 )
			__DEF_COLOR( Orchid,				0xDA70D6 )
			__DEF_COLOR( PaleGoldenRod,			0xEEE8AA )
			__DEF_COLOR( PaleGreen,				0x98FB98 )
			__DEF_COLOR( PaleTurquoise,			0xAFEEEE )
			__DEF_COLOR( PaleVioletRed,			0xDB7093 )
			__DEF_COLOR( PapayaWhip,			0xFFEFD5 )
			__DEF_COLOR( PeachPuff,				0xFFDAB9 )
			__DEF_COLOR( Peru,					0xCD853F )
			__DEF_COLOR( Pink,					0xFFC0CB )
			__DEF_COLOR( Plum,					0xDDA0DD )
			__DEF_COLOR( PowderBlue,			0xB0E0E6 )
			__DEF_COLOR( Purple,				0x800080 )
			__DEF_COLOR( Red,					0xFF0000 )
			__DEF_COLOR( RosyBrown,				0xBC8F8F )
			__DEF_COLOR( RoyalBlue,				0x4169E1 )
			__DEF_COLOR( SaddleBrown,			0x8B4513 )
			__DEF_COLOR( Salmon,				0xFA8072 )
			__DEF_COLOR( SandyBrown,			0xF4A460 )
			__DEF_COLOR( SeaGreen,				0x2E8B57 )
			__DEF_COLOR( SeaShell,				0xFFF5EE )
			__DEF_COLOR( Sienna,				0xA0522D )
			__DEF_COLOR( Silver,				0xC0C0C0 )
			__DEF_COLOR( SkyBlue,				0x87CEEB )
			__DEF_COLOR( SlateBlue,				0x6A5ACD )
			__DEF_COLOR( SlateGray,				0x708090 )
			__DEF_COLOR( Snow,					0xFFFAFA )
			__DEF_COLOR( SpringGreen,			0x00FF7F )
			__DEF_COLOR( SteelBlue,				0x4682B4 )
			__DEF_COLOR( Tan,					0xD2B48C )
			__DEF_COLOR( Teal,					0x008080 )
			__DEF_COLOR( Thistle,				0xD8BFD8 )
			__DEF_COLOR( Tomato,				0xFF6347 )
			__DEF_COLOR( Turquoise,				0x40E0D0 )
			__DEF_COLOR( Violet,				0xEE82EE )
			__DEF_COLOR( Wheat,					0xF5DEB3 )
			__DEF_COLOR( White,					0xFFFFFF )
			__DEF_COLOR( WhiteSmoke,			0xF5F5F5 )
			__DEF_COLOR( Yellow,				0xFFFF00 )
			__DEF_COLOR( YellowGreen,			0x9ACD32 )

#			undef __DEF_COLOR
		};
	};


	struct color4f : fvec4
	{
		color4f() {}
		color4f(const color4u &c);

		explicit
		color4f(float val): fvec4( val ) {}
		
		explicit
		color4f(const float *p): fvec4( p ) {}

		color4f(float R, float G, float B, float A): fvec4( R, G, B, A) {}
		
		template <typename T>
		color4f(const TVec<T,4> &v): fvec4( ColorUtils::Convert<float>( v ) ) {}

		operator fvec4 & ()				{ return ReferenceCast< fvec4 >( *this ); }
		operator fvec4   () const		{ return fvec4( x, y, z, w ); }

		value_t		R ()	const		{ return (*this)[0]; }
		value_t &	R ()				{ return (*this)[0]; }
		value_t		G ()	const		{ return (*this)[1]; }
		value_t &	G ()				{ return (*this)[1]; }
		value_t		B ()	const		{ return (*this)[2]; }
		value_t &	B ()				{ return (*this)[2]; }
		value_t		A ()	const		{ return (*this)[3]; }
		value_t &	A ()				{ return (*this)[3]; }
	};

	
	inline color4u::color4u(const color4f &c): u8_vec4( ColorUtils::Convert<uint8>( (fvec4)c ) ) {}
	inline color4f::color4f(const color4u &c): fvec4( ColorUtils::Convert<float>( (u8_vec4)c ) ) {}

	


	template <typename T, usize I>
	inline TVec<T,I>  ColorUtils::Saturate(const TVec<T,I> &v)
	{
		return Clamp( v, Limit<T>::Min(), Limit<T>::Max() );
	}


	template <typename T, usize I>
	inline TVec<T,I>  ColorUtils::Normalize(const TVec<T,I> &v)
	{
		typedef typename TypeDescriptor::MainType< T, float >::type		main_t;
		
		static const main_t		s_tAdd = main_t( TypeDescriptor::IsInteger<T>::value ? 0.5 : 0 );
		const main_t			t_max  = v.Max();

		if ( IsZero( t_max ) )
			return v;

		TVec<T,I>	ret;
		FOR( i, ret )	ret[i] = T( v[i] / t_max + s_tAdd );
		return ret;
	}

	
	template <typename T, usize I>
	inline TVec<T,I>  ColorUtils::Random()
	{
		TVec<T,I>	ret;
		FOR( i, ret )	ret[i] = UXMath::Random::Range( Limit<T>::Min(), Limit<T>::Max() );
		return ret;
	}
	

	template <typename T, usize I>
	inline TVec<T,I>  ColorUtils::Random (const TVec<T,I> &min, const TVec<T,I> &max)
	{
		ASSERT( All( min >= TVec<T,I>( Limit<T>::Min() ) ) );
		ASSERT( All( max <= TVec<T,I>( Limit<T>::Max() ) ) );

		TVec<T,I>	ret;
		FOR( i, ret )	ret[i] = UXMath::Random::Range( min, max );
		return ret;
	}


	template <typename T, usize I>
	inline T  ColorUtils::Luminance(const TVec<T,I> &v)
	{
		return Dot( TVec<T,3>( v ), LuminanceK<T>::Get() );
	}


	template <typename T, usize I>
	inline T  ColorUtils::LuminanceNorm(const TVec<T,I> &v)
	{
		return Clamp( Luminance( v ), Limit<T>::Min(), Limit<T>::Max() );
	}


	template <typename T, usize I>
	inline T  ColorUtils::Negative(const TVec<T,I> &v)
	{
		TVec<T,I>	ret;
		FOR( i, ret )	ret[i] = i < 3 ? Limit<T>::Max() - v[i] : v[i];
		return ret;
	}


	template <typename T, usize I>
	inline T  ColorUtils::NegativeAlpha(const TVec<T,I> &v)
	{
		return TVec<T,I>( Limit<T>::Max() ) - v;
	}


	template <typename T, usize I>
	inline T  ColorUtils::AdjustContrast(const TVec<T,I> &v, T k)
	{
		static const T		s_tMiddle	= Limit<T>::Max() / 2;
		TVec<T,I>	ret;
		FOR( i, ret )	ret[i] = i < 3 ? s_tMiddle + k * ( v[i] - s_tMiddle ) : v[i];
		return ret;
	}


	template <typename T, usize I>
	inline T  ColorUtils::AdjustSaturation(const TVec<T,I> &v, T k)
	{
		const T		lum		= Luminance( v );
		TVec<T,I>	ret;
		FOR( i, ret )	ret[i] = i < 3 ? lum + k * ( v[i] - lum ) : v[i];
		return ret;
	}

		
	template <typename T, usize I>
	inline TVec<T,I>  ColorUtils::ToSRGB(const TVec<T,I> &v)
	{
		TVec<T,I>	ret;
		FOR( i, ret )	ret[i] = i < 3 ? ( v[i] <= T(0.0031308) ?  T(12.92) * v[i] :
											T(1.055) * Pow( v[i], T(0.41666) ) - T(0.0055) ) : T(0);
		return ret;
	}


	template <typename T, usize I>
	inline TVec<T,I>  ColorUtils::FromSRGB(const TVec<T,I> &v)
	{
		TVec<T,I>	ret;
		FOR( i, ret )	ret[i] = i < 3 ? ( v[i] <= T(0.04045) ?  v[i] / T(12.92) :
											Pow( ( v[i] + T(0.055) ) / T(1.055), T(0.41666) ) ) : T(0);
		return ret;
	}
	

	template <typename T>
	inline TVec<T,4>  ColorUtils::FromRGBA8(uint32 uRGBA)
	{
		return Convert<T>( FromRGBA8<uint8>( uRGBA ) );
	}
	

	template <>
	inline TVec<uint8,4>  ColorUtils::FromRGBA8<uint8>(uint32 uRGBA)
	{
		const uint8 *	p_rgba = (const uint8 *)&uRGBA;
		return TVec<uint8,4>( p_rgba[3], p_rgba[2], p_rgba[1], p_rgba[0] );
	}


	template <typename T>
	inline uint32  ColorUtils::ToRGBA8(const TVec<T,4> &v)
	{
		return ToRGBA8<uint8>( Convert<uint8>( v ) );
	}


	template <>
	inline uint32  ColorUtils::ToRGBA8<uint8>(const TVec<uint8,4> &v)
	{
		uint32	u_rgba = 0;
		uint8 *	p_rgba = (uint8 *)&u_rgba;

		p_rgba[0] = v[3];
		p_rgba[1] = v[2];
		p_rgba[2] = v[1];
		p_rgba[3] = v[0];

		return u_rgba;
	}


	template <typename T>
	inline TMatrix<T,4,4>  ColorUtils::SaturationMat(const T s)
	{
		const TVec<T,3>		c = (T(1) - s) * LuminanceK<T>::Get();

		TMatrix<T,4,4>		ret( 1 );

		ret(0,0) = c[0] + s;	ret(0,1) = c[0];		ret(0,2) = c[0];
		ret(1,0) = c[1];		ret(1,1) = c[1] + s;	ret(1,2) = c[1];
		ret(2,0) = c[2];		ret(2,1) = c[2];		ret(2,2) = c[2] + s;

		return ret;
	}


	template <typename B, typename T, usize I>
	inline TVec<B,I>  ColorUtils::Convert(const TVec<T,I> &v)
	{
		typedef typename TypeDescriptor::MainType3< B, T, float >::type		main_t;

		static const main_t		s_tK   = (main_t)Limit<B>::Max() / (main_t)Limit<T>::Max();
		static const main_t		s_tAdd = main_t( TypeDescriptor::IsInteger<B>::value ? 0.5 : 0 );

		TVec<B,I>	ret;
		FOR( i, ret )	ret[i] = B( v[i] * s_tK + s_tAdd );
		return ret;
	}

//-------------------------------------------------------------------

}	// UXMath
}	// UX_STL

#endif	// _UXS_COLOR_H